import Cesium from "../../Ces/Cesium"
import WeDataParserBase from "../../Display/WeDataParserBase"
import WeContext from "../../Core/WeContext"

class Swan2Parser extends WeDataParserBase {
  requestImage(url: string, getUrlCallback: any) {
    if (getUrlCallback) url = getUrlCallback(url) + ''
    return Cesium.Resource.fetchArrayBuffer(url).then(async (arraybuffer) => {
      const typeArray = new Uint8Array(arraybuffer)
      return this.decompressAndParse(typeArray)
    })
  }

  async decompressAndParse(typeArray: Uint8Array) {
    //bz2 magic
    if (typeArray[0] === 0x42 && typeArray[1] === 0x5a) {
      const weContext: WeContext = this.weContext
      const processor = await weContext.taskProcessorPool.getTaskProcessor(0)
      const result: any = await processor.scheduleTask(
        {
          type: 'decompressbz2',
          typeArray,
        },
        [typeArray.buffer],
      )
      return this.parse(result)
    }
    return this.parse(typeArray)
  }

  parse(uint8Array: Uint8Array) {
    const parseString = (uint8Array, byteOffset, length) => {
      return Cesium.getStringFromTypedArray(uint8Array, byteOffset, length).replace(
        // eslint-disable-next-line no-control-regex
        /\/u0000|[\u0000\x00]/g,
        '',
      )
    }
    const view = new DataView(uint8Array.buffer)

    let byteOffset = 0
    const ZonName = parseString(uint8Array, byteOffset, 12)
    byteOffset += 12
    const DataName = parseString(uint8Array, byteOffset, 38)
    byteOffset += 38
    const Flag = parseString(uint8Array, byteOffset, 8)
    byteOffset += 8
    const Version = parseString(uint8Array, byteOffset, 8)
    byteOffset += 8

    const year = view.getUint16(byteOffset, true)
    byteOffset += 2
    const month = view.getUint16(byteOffset, true)
    byteOffset += 2
    const day = view.getUint16(byteOffset, true)
    byteOffset += 2
    const hour = view.getUint16(byteOffset, true)
    byteOffset += 2
    const minute = view.getUint16(byteOffset, true)
    byteOffset += 2
    const interval = view.getUint16(byteOffset, true)
    byteOffset += 2

    const XNumGrids = view.getUint16(byteOffset, true)
    byteOffset += 2
    const YNumGrids = view.getUint16(byteOffset, true)
    byteOffset += 2
    const ZNumGrids = view.getUint16(byteOffset, true)
    byteOffset += 2

    const RadarCount = view.getUint32(byteOffset, true)
    byteOffset += 4

    const StartLon = view.getFloat32(byteOffset, true)
    byteOffset += 4
    const StartLat = view.getFloat32(byteOffset, true)
    byteOffset += 4
    const CenterLon = view.getFloat32(byteOffset, true)
    byteOffset += 4
    const CenterLat = view.getFloat32(byteOffset, true)
    byteOffset += 4

    const XReso = view.getFloat32(byteOffset, true)
    byteOffset += 4

    const YReso = view.getFloat32(byteOffset, true)
    byteOffset += 4

    const ZhighGrids: number[] = []
    for (let i = 0; i < 40; i++) {
      ZhighGrids.push(view.getFloat32(byteOffset, true))
      byteOffset += 4
    }

    const RadarStationName: string[] = []
    for (let i = 0; i < 20; i++) {
      const decoder = new TextDecoder('gbk')
      const chineseText = decoder.decode(uint8Array.subarray(byteOffset, byteOffset + 16))
      // eslint-disable-next-line no-control-regex
      RadarStationName.push(chineseText.replace(/\/u0000|[\u0000\x00]/g, ''))
      byteOffset += 16
    }

    const RadarLongitude: number[] = []
    const RadarLatitude: number[] = []
    const RadarAltitude: number[] = []
    const MosaicFlag: number[] = []
    for (let i = 0; i < 20; i++) {
      RadarLongitude.push(view.getFloat32(byteOffset, true))
      RadarLatitude.push(view.getFloat32(byteOffset + 20 * 4, true))
      RadarAltitude.push(view.getFloat32(byteOffset + 40 * 4, true))
      MosaicFlag.push(view.getUint8(byteOffset + 60 * 4))
      byteOffset += 4
    }
    byteOffset += 20 * 4
    byteOffset += 20 * 4
    byteOffset += 20
    byteOffset += 172 // Reserved

    const arrayBufferView = uint8Array.subarray(byteOffset, uint8Array.length)

    const width = XNumGrids
    const height = YNumGrids
    const depth = ZNumGrids

    const west = StartLon
    const east = west + width * XReso
    const north = StartLat
    const south = north - height * YReso

    const externalData = {
      ZonName,
      DataName,
      Version,
      Flag,
      year,
      month,
      day,
      hour,
      minute,
      interval,
      XNumGrids,
      YNumGrids,
      ZNumGrids,
      RadarCount,
      StartLon,
      StartLat,
      CenterLon,
      CenterLat,
      XReso,
      YReso,
      ZhighGrids,
      RadarStationName,
      RadarLongitude,
      RadarLatitude,
      RadarAltitude,
      MosaicFlag,
      size: {
        depth: ZhighGrids[depth - 1] * 1000,
      },
    }

    return {
      externalData,
      west,
      south,
      east,
      north,
      source: {
        width,
        height,
        depth,
        pixelFormat: Cesium.PixelFormat.ALPHA,
        pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE,
        arrayBufferView,
      },
    }
  }
}

export default Swan2Parser
